<?php
/**
 * @version $Id: gticket.php 181 2008-03-11 21:46:28Z mikhail.miguel $
 */

// GIJOE's Ticket Class (based on Marijuana's Oreteki XOOPS)
// nobunobu's suggestions are applied

if( ! class_exists( 'XoopsGTicket' ) ) {

class XoopsGTicket {

    var $_errors = array() ;
    var $_latest_token = '' ;
    var $messages = array() ;

    function XoopsGTicket()
    {
        global $xoopsConfig ;

        // language file
        if( defined( 'XOOPS_ROOT_PATH' ) && ! empty( $xoopsConfig['language'] ) && ! strstr( $xoopsConfig['language'] , '/' ) ) {
            if( file_exists( dirname( dirname( __FILE__ ) ) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml' ) ) {
                include dirname( dirname( __FILE__ ) ) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml' ;
            }
        }

        // default messages
        if( empty( $this->messages ) ) $this->messages = array(
            'err_general' => 'GTicket Error' ,
            'err_nostubs' => 'No stubs found' ,
            'err_noticket' => 'No ticket found' ,
            'err_nopair' => 'No valid ticket-stub pair found' ,
            'err_timeout' => 'Time out' ,
            'err_areaorref' => 'Invalid area or referer' ,
            'fmt_prompt4repost' => 'error(s) found:<br /><span style="background-color:red;font-weight:bold;color:white;">%s</span><br />Confirm it.<br />And do you want to post again?' ,
            'btn_repost' => 'repost' ,
        ) ;
    }

    // render form as plain html
    function getTicketHtml( $salt = '' , $timeout = 1800 , $area = '' )
    {
        return '<input type="hidden" name="XOOPS_G_TICKET" value="'.$this->issue( $salt , $timeout , $area ).'" />' ;
    }

    // returns an object of XoopsFormHidden including theh ticket
    function getTicketXoopsForm( $salt = '' , $timeout = 1800 , $area = '' )
    {
        return new XoopsFormHidden( 'XOOPS_G_TICKET' , $this->issue( $salt , $timeout , $area ) ) ;
    }

    // add a ticket as Hidden Element into XoopsForm
    function addTicketXoopsFormElement( &$form , $salt = '' , $timeout = 1800 , $area = '' )
    {
        $form->addElement( new XoopsFormHidden( 'XOOPS_G_TICKET' , $this->issue( $salt , $timeout , $area ) ) ) ;
    }

    // returns an array for xoops_confirm() ;
    function getTicketArray( $salt = '' , $timeout = 1800 , $area = '' )
    {
        return array( 'XOOPS_G_TICKET' => $this->issue( $salt , $timeout , $area ) ) ;
    }

    // return GET parameter string.
    function getTicketParamString( $salt = '' , $noamp = false , $timeout=1800 , $area = '' )
    {
        return ( $noamp ? '' : '&amp;' ) . 'XOOPS_G_TICKET=' . $this->issue( $salt, $timeout , $area ) ;
    }

    // issue a ticket
    function issue( $salt = '' , $timeout = 1800 , $area = '' )
    {
        global $xoopsModule ;
    
        // create a token
        list( $usec , $sec ) = explode( " " , microtime() ) ;
        $appendix_salt = empty( $_SERVER['PATH'] ) ? XOOPS_DB_NAME : $_SERVER['PATH'] ;
        $token = crypt( $salt . $usec . $appendix_salt . $sec ) ;
        $this->_latest_token = $token ;

        if( empty( $_SESSION['XOOPS_G_STUBS'] ) ) $_SESSION['XOOPS_G_STUBS'] = array() ;

        // limit max stubs 10
        if( sizeof( $_SESSION['XOOPS_G_STUBS'] ) > 10 ) {
            $_SESSION['XOOPS_G_STUBS'] = array_slice( $_SESSION['XOOPS_G_STUBS'] , -10 ) ;
        }

        // record referer if browser send it
        $referer = empty( $_SERVER['HTTP_REFERER'] ) ? '' : $_SERVER['REQUEST_URI'] ;

        // area as module's dirname
        if( ! $area && is_object( @$xoopsModule ) ) {
            $area = $xoopsModule->getVar('dirname') ;
        }

        // store stub
        $_SESSION['XOOPS_G_STUBS'][] = array(
            'expire' => time() + $timeout ,
            'referer' => $referer ,
            'area' => $area ,
            'token' => $token
        ) ;

        // paid md5ed token as a ticket
        return md5( $token . XOOPS_DB_PREFIX ) ;
    }

    // check a ticket
    function check( $post = true , $area = '' , $allow_repost = true )
    {
        global $xoopsModule ;

        $this->_errors = array() ;

        // CHECK: stubs are not stored in session
        if( ! is_array(@$_SESSION['XOOPS_G_STUBS'])) {
            $this->_errors[] = $this->messages['err_nostubs'] ;
            $_SESSION['XOOPS_G_STUBS'] = array() ;
        }

        // get key&val of the ticket from a user's query
        $ticket = $post ? @$_POST['XOOPS_G_TICKET'] : @$_GET['XOOPS_G_TICKET'] ;

        // CHECK: no tickets found
        if( empty( $ticket ) ) {
            $this->_errors[] = $this->messages['err_noticket'] ;
        }

        // gargage collection & find a right stub
        $stubs_tmp = $_SESSION['XOOPS_G_STUBS'] ;
        $_SESSION['XOOPS_G_STUBS'] = array() ;
        foreach( $stubs_tmp as $stub ) {
            // default lifetime 30min
            if( $stub['expire'] >= time() ) {
                if( md5( $stub['token'] . XOOPS_DB_PREFIX ) === $ticket ) {
                    $found_stub = $stub ;
                } else {
                    // store the other valid stubs into session
                    $_SESSION['XOOPS_G_STUBS'][] = $stub ;
                }
            } else {
                if( md5( $stub['token'] . XOOPS_DB_PREFIX ) === $ticket ) {
                    // not CSRF but Time-Out
                    $timeout_flag = true ;
                }
            }
        }

        // CHECK: the right stub found or not
        if( empty( $found_stub ) ) {
            if( empty( $timeout_flag ) ) $this->_errors[] = $this->messages['err_nopair'] ;
            else $this->_errors[] = $this->messages['err_timeout'] ;
        } else {

            // set area if necessary
            // area as module's dirname
            if( ! $area && is_object( @$xoopsModule ) ) {
                $area = $xoopsModule->getVar('dirname') ;
            }

            // check area or referer
            if( @$found_stub['area'] == $area ) $area_check = true ;
            if( ! empty( $found_stub['referer'] ) && strstr( @$_SERVER['HTTP_REFERER'] , $found_stub['referer'] ) ) $referer_check = true ;

            if( empty( $area_check ) && empty( $referer_check ) ) { // loose
                $this->_errors[] = $this->messages['err_areaorref'] ;
            }
        }

        if( ! empty( $this->_errors ) ) {
            if( $allow_repost ) {
                // repost form
                $this->draw_repost_form( $area ) ;
                exit ;
            } else {
                // failed
                $this->clear() ;
                return false ;
            }
        } else {
            // all green
            return true;
        }
    }

    // draw form for repost
    function draw_repost_form( $area = '' )
    {
        // Notify which file is broken
        if( headers_sent() ) {
            restore_error_handler() ;
            set_error_handler( 'GTicket_ErrorHandler4FindOutput' ) ;
            header( 'Dummy: for warning' ) ;
            restore_error_handler() ;
            exit ;
        }
    
        error_reporting( 0 ) ;
        while( ob_get_level() ) ob_end_clean() ;

        $table = '<table>' ;
        $form = '<form action="?'.htmlspecialchars(@$_SERVER['QUERY_STRING'],ENT_QUOTES).'" method="post" >' ;
        foreach( $_POST as $key => $val ) {
            if( $key == 'XOOPS_G_TICKET' ) continue ;
            if( get_magic_quotes_gpc() ) {
                $key = stripslashes( $key ) ;
            }
            if( is_array( $val ) ) {
                list( $tmp_table , $tmp_form ) = $this->extract_post_recursive( htmlspecialchars($key,ENT_QUOTES) , $val ) ;
                $table .= $tmp_table ;
                $form .= $tmp_form ;
            } else {
                if( get_magic_quotes_gpc() ) {
                    $val = stripslashes( $val ) ;
                }
                $table .= '<tr><th>'.htmlspecialchars($key,ENT_QUOTES).'</th><td>'.htmlspecialchars($val,ENT_QUOTES).'</td></tr>'."\n" ;
                $form .= '<input type="hidden" name="'.htmlspecialchars($key,ENT_QUOTES).'" value="'.htmlspecialchars($val,ENT_QUOTES).'" />'."\n" ;
            }
        }
        $table .= '</table>' ;
        $form .= $this->getTicketHtml(__LINE__,300,$area).'<input type="submit" value="'.$this->messages['btn_repost'].'" /></form>' ;

        echo '<html><head><title>'.$this->messages['err_general'].'</title><style>table,td,th {border:solid black 1px; border-collapse:collapse;}</style></head><body>' . sprintf( $this->messages['fmt_prompt4repost'] , $this->getErrors() ) . $table . $form . '</body></html>' ;
    }

    function extract_post_recursive( $key_name , $tmp_array ) {
        $table = '' ;
        $form = '' ;
        foreach( $tmp_array as $key => $val ) {
            if( get_magic_quotes_gpc() ) {
                $key = stripslashes( $key ) ;
            }
            if( is_array( $val ) ) {
                list( $tmp_table , $tmp_form ) = $this->extract_post_recursive( $key_name.'['.htmlspecialchars($key,ENT_QUOTES).']' , $val ) ;
                $table .= $tmp_table ;
                $form .= $tmp_form ;
            } else {
                if( get_magic_quotes_gpc() ) {
                    $val = stripslashes( $val ) ;
                }
                $table .= '<tr><th>'.$key_name.'['.htmlspecialchars($key,ENT_QUOTES).']</th><td>'.htmlspecialchars($val,ENT_QUOTES).'</td></tr>'."\n" ;
                $form .= '<input type="hidden" name="'.$key_name.'['.htmlspecialchars($key,ENT_QUOTES).']" value="'.htmlspecialchars($val,ENT_QUOTES).'" />'."\n" ;
            }
        }
        return array( $table , $form ) ;
    }


    // clear all stubs
    function clear()
    {
        $_SESSION['XOOPS_G_STUBS'] = array() ;
    }


    // Ticket Using
    function using()
    {
        if( ! empty( $_SESSION['XOOPS_G_STUBS'] ) ) {
            return true;
        } else {
            return false;
        }
    }


    // return errors
    function getErrors( $ashtml = true )
    {
        if( $ashtml ) {
            $ret = '' ;
            foreach( $this->_errors as $msg ) {
                $ret .= "$msg<br />\n" ;
            }
        } else {
            $ret = $this->_errors ;
        }
        return $ret ;
    }

// end of class
}

function GTicket_ErrorHandler4FindOutput($errNo, $errStr, $errFile, $errLine)
{
    if( preg_match( '?'.preg_quote(XOOPS_ROOT_PATH).'([^:]+)\:(\d+)?' , $errStr , $regs ) ) {
        echo "Irregular output! check the file ".htmlspecialchars($regs[1])." line ".htmlspecialchars($regs[2]) ;
    } else {
        echo "Irregular output! check language files etc." ;
    }
    return ;
}

// create a instance in global scope
$GLOBALS['xoopsGTicket'] = new XoopsGTicket() ;

}

if( ! function_exists( 'admin_refcheck' ) ) {

//Admin Referer Check By Marijuana(Rev.011)
function admin_refcheck($chkref = "") {
    if( empty( $_SERVER['HTTP_REFERER'] ) ) {
        return true ;
    } else {
        $ref = $_SERVER['HTTP_REFERER'];
    }
    $cr = XOOPS_URL;
    if ( $chkref != "" ) { $cr .= $chkref; }
    if ( strpos($ref, $cr) !== 0 ) { return false; }
    return true;
}

}


?>